/*
 * Funny Language - a free style programming language.
 * Copyright (C) 2015 by fanguangping (fanguangping@163.com)
 * dfa.c
 */

#include <stdlib.h>
#include <string.h>
#include "common.h"

String match_one_arg(Statement *source) {
	Queue *functions = get_functions();
	Mapping *mapping = NULL;
	String s = NULL;
	QueueNode *node = NULL;

    String symbol = get_arg_name(source);

	if (contains_queue_element(functions, symbol, equals_string) == TRUE) {
		return symbol;
	}

    String lex = expand_lex(symbol);
    if (lex != NULL) {
        return lex;
    }

	Queue *mappings = get_mappings(source);
	if (mappings != NULL) {
		//MAPPINGS
		node = mappings->head;
		while (node != NULL) {
			mapping = (Mapping *) node->element;
			if (mapping->sys != NULL) {
				node = node->next;
				continue;
			}

			//TODO return lex(mapping->targetStatement);
			if (equal_arg(mapping->sourceStatement, symbol)) {
				s = extract(mapping->targetStatement);
				//return s;
				return expand_lex(s);
			}

			if (mapping->functionName != NULL && strcmp(symbol, mapping->functionName) == 0) {
				return mapping->functionName;
			}

			node = node->next;
		}
	}

	return NULL;
}

int orignal_statement(Statement *source) {
	QueueNode *node = NULL;
	Statement *s = NULL;
	if (source->children != NULL && source->children->head != NULL) {
		node = source->children->head;
		while (node != NULL) {
			s = (Statement *) node->element;
			if (s->type != SYM) {
				return FALSE;
			}
			node = node->next;
		}
	}

	return TRUE;
}

String match_orignal(Statement *source) {
	int orignal = TRUE;
	Queue *queue = NULL;
	QueueNode *node = NULL;
	Statement *s = NULL;
	String result = init_string();

	Queue *mappings = get_mappings(source);
	Mapping *mapping = NULL;

	if (mappings != NULL) {
		//MAPPINGS
		node = mappings->head;
		while (node != NULL) {
			mapping = (Mapping *) node->element;
			if (mapping->sys != NULL) {
				node = node->next;
				continue;
			}

			if (equal_statement(mapping->sourceStatement, source)) {
				return extract(mapping->targetStatement);
			}

			node = node->next;
		}
	}

	queue = source->children;
	node = queue->head;
	while (node != NULL) {
		s = (Statement *) node->element;
		if (s->type == SYM) {
			result = dyna_strcat(result, s->symbol);
			result = dyna_strcat(result, " ");
		} else {
			orignal = FALSE;
			break;
		}

		node = node->next;
	}

	if (orignal) {
		return result;
	} else {
		free(result);
		return NULL;
	}
}

String match(Statement *source) {
	QueueNode *node = NULL;
	Mapping *mapping = NULL;
	String result = NULL;
	State *start = NULL;
	Queue *repQueue = NULL;
	Statement *expanded = NULL;

	// match one arg
	if (source->children != NULL && source->children->head != NULL
			&& source->children->head == source->children->tail) {
		//temp = extract(source);
		result = match_one_arg(source);
		if (result != NULL) {
			return result;
		}
	}

	// match orignal
	if (orignal_statement(source)) {
		result = match_orignal(source);
		if (result != NULL) {
			return result;
		}
	}

	Queue *mappings = get_mappings(source);
	if (mappings != NULL) {
		node = mappings->head;
		while (node != NULL) {
			mapping = (Mapping *) node->element;
			repQueue = init_queue();

			// trace_mapping(mapping);

			start = test_match(mapping, source, repQueue);
			if (start == NULL) {
				node = node->next;
				continue;
			}
			if (start->accept) {
				if (mapping->isMacro) {
					expanded = expand_statement(mapping, repQueue);
					String string = to_string_statement(expanded);
					printf("expanded: %s\n", string);
					return match(expanded);
				}

				if (mapping->sys != NULL) {
					return eval_sys(mapping, repQueue);
				} else {
					result = eval_normal(mapping, repQueue);
				}
			}

			node = node->next;
		}
	}

	if (result == NULL) {
		printf("Cannot find mapping for statement: %s\n", to_string_statement(source));
	}

	return result;
}

static String match_loop_statement(Statement *loop, Queue *repQueue, int matchTimes) {
	int i = 0;
	int index = 0;
	QueueNode *node = NULL;
	Statement *s = NULL;
	Statement *st = NULL;
	String result = init_string();
	Queue *loopQueue = NULL;
	QueueNode *loopQueueNode = NULL;

	for (i = 0; i < matchTimes; i++) {
		if (loop != NULL) {
			node = loop->children->head;
			while (node != NULL) {
				result = dyna_strcat(result, " ");

				s = (Statement *) node->element;
				if (s->type == ARG) {
					index = 0;

					loopQueue = find_values(repQueue, get_arg_name(s));
					loopQueueNode = loopQueue->head;
					while (loopQueueNode != NULL) {
						if (index == i) {
							st = (Statement *) get_queue_element(loopQueue, index);
							//if (strcmp("{}", to_string_statement(st)) == 0) {
							if (empty_statement(st)) {
								return NULL;
							}
							result = dyna_strcat(result, match(st));
							break;
						}
						index++;
						loopQueueNode = loopQueueNode->next;
					}
				} else {
					result = dyna_strcat(result, to_string_statement(s));
				}
				node = node->next;
			}
		}
	}

	return result;
}

String eval_normal(Mapping *mapping, Queue *repQueue) {
	String result = init_string();
	QueueNode *node = NULL;
	Statement *s = NULL;
	String temp = NULL;

	String loopBuffer = NULL;
	int matchTimes = 0;

	node = mapping->targetStatement->children->head;
	while (node != NULL) {
		s = (Statement *) node->element;
		temp = to_string_statement(s);

		switch (s->type) {
		case SYM:
			result = dyna_strcat(result, temp);
			result = dyna_strcat(result, " ");
			break;

		case ARG:
			result = dyna_strcat(result, match((Statement *)find_value(repQueue, get_arg_name(s))));
			result = dyna_strcat(result, " ");
			break;

		case LOOP:
			matchTimes = get_match_times(s, repQueue);
			loopBuffer = match_loop_statement(s, repQueue, matchTimes);
			if (loopBuffer != NULL) {
				result = dyna_strcat(result, loopBuffer);
			}
			break;

		default:
			break;
		}

		node = node->next;
	}

	return result;
}
